#include <Master.hpp>
#include <iostream>
#include <Trie.hpp>
#include <sstream>
#include <Utility.hpp>
#include <cstdlib>
#include <ctime>
#include <GTrie.hpp>
#include <mpi.h>

Master::Master(std::istream& source, int np) {
	this->source = &source;
	this->np 	 = np;
}

void Master::run() {

	Trie * trie = new Trie();
	int qtyWords = trie->deserialize(*source);

	std::cout << "There are " << qtyWords << 
		" distinct words\n";

	std::vector<std::string> words;

	trie->itemize(words);

	std::vector<int> remaining(words.size());

	delete trie;
	trie = new Trie();
	GTrie<int>  * gtrie = new GTrie<int>();

	Utility::split(trie, gtrie, words, remaining);

	int qtyParts;
	{
		std::stringstream ss;
		trie->serialize(ss);
		qtyParts = trie->deserialize(ss);
	}

	std::vector<std::string> parts;

	trie->itemize(parts);

	std::cout << "There are " << qtyParts << 
		" distinct parts\n";

	delete trie;
	for (int proc = 1; proc < np; proc++) {
		trie = new Trie();

		for (int i = proc, k = np;
		 	 i < qtyParts; i += k) {
			trie->insert(parts[i]);
		}

		std::stringstream ss;
		trie->serialize(ss);

		std::string str;
		ss >> str;

		int sz = str.size();

		MPI::COMM_WORLD.Send(&sz, 1, MPI_INT, proc, 1);
		MPI::COMM_WORLD.Send(str.c_str(), sz,
							 MPI_CHAR, proc, 1);

		delete trie;
	}

	trie = new Trie();
	int myqty = 0;
	for (int i = 0, k = np; i < qtyParts; i += k)
		trie->insert(parts[i]), myqty++;

	std::cout << "Master received "
		<< myqty << " parts\n";

	for (int found = 0; found < myqty; ) {
		std::string word = Utility::randomWord(true);
		std::vector<std::string> search = 
			trie->retrieveUntil(word);
		if (search.size()) {
			found++;
			trie->remove(word);
		}
	}

	delete trie;

	std::cout << "Master finished. Waiting for others\n";

	MPI::COMM_WORLD.Barrier();

	std::cout << "Very good!\n";

}
